#include <stdlib.h>
#include <stdio.h>
#include <assert.h>

#include "DLinkList.h"

#define   DEADBEEF          (void*)(0xdeadbeef)

typedef struct Node_t
{
    struct Node_t *prev,*next;
    void *data;
}   Node;

struct List_t
{
    Node stabHead,stabTail;     /*stab are phonies. prior to real hand and post real tail*/
};

/*create new node*/
static void    NodeDel(Node *_node);
/*delete new  node (not user data!, only inner list allocations)*/
static Node*   NodeNew(const  void *_item);
/*common function to all single pushes*/
static int     InsertBeforeNode(Node *_node, const  void* _data);
/*common function to all single Pops*/
static void    RemoveNode (Node *_node);
/*common function to multiple pushes after some node*/
static int     MultiPushBeforeNode(Node *_rightNode, size_t _elmnSize,size_t _numElmn,const void* _itemsData);

/***********************************************************************************************************/
/**********************************         API's   (user functions)    ************************************/
/***********************************************************************************************************/

/*Create new list Data Structure*/
List*  ListNew(void)
{
    List *list;
    list = (List*) malloc( sizeof(List) );
    assert(list != 0);
    list->stabHead.data = DEADBEEF ;
    list->stabTail.data = DEADBEEF ;
    list->stabHead.prev = 0 ;
    list->stabTail.next = 0 ;
    list->stabHead.next = &(list->stabTail);
    list->stabTail.prev = &(list->stabHead);
    return list;
}

/*Destroy list Data Structure (user keep data)*/
void  ListDel(List* _list)
{
    Node *node, *nodeNext;
    node  =  nodeNext = 0;
    #ifdef _DEBUG
    assert (0 != _list);
    #endif /* _DEBUG*/
    node     = &_list->stabHead;
    nodeNext = node->next;
    while (node->next != &(_list->stabTail))
    {
        NodeDel(node->next);
        node = nodeNext->next;
        nodeNext = nodeNext->next->next;
    }
    free ( _list);
}

/*add new item to end of list*/
int  ListPushH(List* _list, const  void* _itemData)
{
    #ifdef _DEBUG
    assert (0 != _list);
    assert (0 != _itemData);
    #endif /* _DEBUG*/
    return InsertBeforeNode(_list->stabHead.next, _itemData);
}

/*add new item to begining of list*/
int  ListPushT(List* _list, const  void* _itemData)
{
    #ifdef _DEBUG
    assert (0 != _list);
    assert (0 != _itemData);
    #endif /* _DEBUG*/
    return InsertBeforeNode(&_list->stabTail, _itemData);
}

/*add new items to beginging of list*/
int  ListPushHM(List* _list,size_t _elmnSize,size_t _numElmn,const void* _itemsData)
{
    #ifdef _DEBUG
    assert (0 != _list);
    assert (0 != _itemsData);
    assert (0 != _elmnSize);
    assert ((long)(_numElmn) > 0);
    #endif /* _DEBUG*/
    return MultiPushBeforeNode(_list->stabHead.next, _elmnSize,_numElmn,_itemsData);
}
/*add new items to end of list*/
int  ListPushTM(List* _list,size_t _elmnSize,size_t _numElmn,const  void* _itemsData)
{
    #ifdef _DEBUG
    assert (0 != _list);
    assert (0 != _itemsData);
    assert (0 != _elmnSize);
    assert ((long)(_numElmn) > 0);
    #endif /* _DEBUG*/
    return MultiPushBeforeNode(&_list->stabTail, _elmnSize, _numElmn,_itemsData);
}
/*get List's Head (first item)*/
void*   ListHead(const List* _list)
{
    #ifdef _DEBUG
    assert (0 != _list);
    #endif /* _DEBUG*/
    return _list->stabHead.next->data;
}

/*get List's Head (Last item)*/
void*   ListTail(const  List* _list)
{
    #ifdef _DEBUG
    assert (0 != _list);
    #endif /* _DEBUG*/
    return _list->stabTail.prev->data;
}

/*delete List's Head (First item)*/
void  ListPopH(List* _list)
{
    #ifdef _DEBUG
    assert (0 != _list);
    #endif /* _DEBUG*/
    RemoveNode (_list->stabHead.next);
}

/*delete List's Tail (Last item)*/
void  ListPopT(List* _list)
{
    #ifdef _DEBUG
    assert (0 != _list);
    #endif /* _DEBUG*/
    RemoveNode (_list->stabTail.prev);
}


/*Find an element using a compare runction of IsMatch type*/
void* ListFindIf(const List* _list,IsMatch_t _CmpFunc,const  void* _param)
{
    int res = 0;
    Node *node =  0 ;
    #ifdef _DEBUG
    assert (0 != _list);
    assert (0 != _param);
    #endif /* _DEBUG*/
    node = _list->stabHead.next;
    while (0 == res  && node)
    {
        res  = _CmpFunc((const void*)(node->data),_param);
        node = node->next;
    }
    if (0 == res)
    {
        return 0;
    }
    return (void*)(node->prev->data);
}

/*Count number of items in list*/
size_t  ListCount(const List* _list)
{
    size_t count = 0;
    Node  *node = 0 ;
    #ifdef _DEBUG
    assert (0 != _list);
    #endif /* _DEBUG*/
    node  = _list->stabTail.prev->prev;
    while (node)
    {
        node = node->prev;
        ++count;
    }
    return count;
}

/*Check if list is empty*/
int     ListIsEmpty(const List* _list)
{
    #ifdef _DEBUG
    assert (0 != _list);
    #endif /* _DEBUG*/
    if (_list->stabHead.next == &_list->stabTail)
    {
        return 1;
    }
    return 0;
}

/***********************************************************************************************************/
/**********************************          STATIC FUNCTIONS           ************************************/
/***********************************************************************************************************/

/*create Node*/
Node*  NodeNew(const void* _itemData)
{
    Node* node =  0 ;
    node = (Node*) malloc (sizeof( Node ));
    if (0 == node) return 0;
    node->data = (void*)(_itemData);
    return node;
}

/*Delete Node : deleting linking not user data! */
void  NodeDel(Node *_node)
{
    free (_node);
}

/*insert node before _node, (i.e., to the "left" of "_rightNode") */
int InsertBeforeNode(Node *_rightNode, const  void* _data)
{
    Node *node = NodeNew(_data);            /*allocate new node before _node */
    if (0 == node)
    {
        return -1;
    }
    node->prev                =  _rightNode->prev;
    _rightNode->prev->next    =   node;
    node->next                =  _rightNode;
    _rightNode->prev           =  node;
    return 0;
}

/*remove node from list */
void    RemoveNode (Node *_node)
{
    _node->prev->next      =     _node->next;
    _node->next->prev      =     _node->prev;
    NodeDel(_node);
}

static int  MultiPushBeforeNode(Node *_rightNode, size_t _elmnSize,size_t _numElmn,const  void* _itemsData)
{
    size_t i;
    int    ret = 0;
    char *data = (char*)(_itemsData);
    for (i = 0 ; i < _numElmn ; ++i)
    {
         ret += InsertBeforeNode(_rightNode, data  + i *_elmnSize);
    }
    return ret;
}
